package com.hqd.ch03.v51.web.context.support;

import com.hqd.ch03.utils.ObjectUtils;
import com.hqd.ch03.v51.beans.factory.config.ConfigurableListableBeanFactory;
import com.hqd.ch03.v51.beans.factory.support.DefaultListableBeanFactory;
import com.hqd.ch03.v51.boot.web.context.ServletContextAware;
import com.hqd.ch03.v51.context.support.GenericApplicationContext;
import com.hqd.ch03.v51.context.ui.Theme;
import com.hqd.ch03.v51.context.ui.ThemeSource;
import com.hqd.ch03.v51.core.env.ConfigurableEnvironment;
import com.hqd.ch03.v51.io.Resource;
import com.hqd.ch03.v51.io.support.ResourcePatternResolver;
import com.hqd.ch03.v51.ui.UiApplicationContextUtils;
import com.hqd.ch03.v51.utils.StringUtils;
import com.hqd.ch03.v51.web.context.ConfigurableWebApplicationContext;
import com.hqd.ch03.v51.web.context.ConfigurableWebEnvironment;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;

public class GenericWebApplicationContext extends GenericApplicationContext
        implements ConfigurableWebApplicationContext, ThemeSource {


    private ServletContext servletContext;

    private ThemeSource themeSource;


    /**
     * Create a new {@code GenericWebApplicationContext}.
     *
     * @see #setServletContext
     * @see #registerBeanDefinition
     * @see #refresh
     */
    public GenericWebApplicationContext() {
        super();
    }

    /**
     * Create a new {@code GenericWebApplicationContext} for the given {@link ServletContext}.
     *
     * @param servletContext the {@code ServletContext} to run in
     * @see #registerBeanDefinition
     * @see #refresh
     */
    public GenericWebApplicationContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    /**
     * Create a new {@code GenericWebApplicationContext} with the given {@link DefaultListableBeanFactory}.
     *
     * @param beanFactory the {@code DefaultListableBeanFactory} instance to use for this context
     * @see #setServletContext
     * @see #registerBeanDefinition
     * @see #refresh
     */
    public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory) {
        super(beanFactory);
    }

    /**
     * Create a new {@code GenericWebApplicationContext} with the given {@link DefaultListableBeanFactory}
     * and {@link ServletContext}.
     *
     * @param beanFactory    the {@code DefaultListableBeanFactory} instance to use for this context
     * @param servletContext the {@code ServletContext} to run in
     * @see #registerBeanDefinition
     * @see #refresh
     */
    public GenericWebApplicationContext(DefaultListableBeanFactory beanFactory, ServletContext servletContext) {
        super(beanFactory);
        this.servletContext = servletContext;
    }

    @Override

    public ServletContext getServletContext() {
        return this.servletContext;
    }

    /**
     * Set the {@link ServletContext} that this {@code WebApplicationContext} runs in.
     */
    @Override
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    @Override
    public String getApplicationName() {
        return (this.servletContext != null ? this.servletContext.getContextPath() : "");
    }

    /**
     * Create and return a new {@link StandardServletEnvironment}.
     */
    @Override
    protected ConfigurableEnvironment createEnvironment() {
        return new StandardServletEnvironment();
    }

    /**
     * Register request/session scopes, environment beans, a {@link ServletContextAwareProcessor}, etc.
     */
    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        if (this.servletContext != null) {
            beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
            beanFactory.ignoreDependencyInterface(ServletContextAware.class);
        }
        WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext);
    }

    /**
     * This implementation supports file paths beneath the root of the {@link ServletContext}.
     *
     * @see ServletContextResource
     */
    @Override
    protected Resource getResourceByPath(String path) {
        return new ServletContextResource(this.servletContext, path);
    }

    @Override
    protected ResourcePatternResolver getResourcePatternResolver() {
        return new ServletContextResourcePatternResolver(this);
    }

    /**
     * Initialize the theme capability.
     */
    @Override
    protected void onRefresh() {
        this.themeSource = UiApplicationContextUtils.initThemeSource(this);
    }

    /**
     * {@inheritDoc}
     * <p>Replace {@code Servlet}-related property sources.
     */
    @Override
    protected void initPropertySources() {
        ConfigurableEnvironment env = getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
        }
    }

    @Override
    public Theme getTheme(String themeName) {
        return this.themeSource.getTheme(themeName);
    }


    // ---------------------------------------------------------------------
    // Pseudo-implementation of ConfigurableWebApplicationContext
    // ---------------------------------------------------------------------

    @Override
    public ServletConfig getServletConfig() {
        throw new UnsupportedOperationException(
                "GenericWebApplicationContext does not support getServletConfig()");
    }

    @Override
    public void setServletConfig(ServletConfig servletConfig) {
        // no-op
    }

    @Override

    public String getNamespace() {
        throw new UnsupportedOperationException(
                "GenericWebApplicationContext does not support getNamespace()");
    }

    @Override
    public void setNamespace(String namespace) {
        // no-op
    }

    @Override
    public void setConfigLocation(String configLocation) {
        if (StringUtils.hasText(configLocation)) {
            throw new UnsupportedOperationException(
                    "GenericWebApplicationContext does not support setConfigLocation(). " +
                            "Do you still have a 'contextConfigLocation' init-param set?");
        }
    }

    @Override
    public String[] getConfigLocations() {
        throw new UnsupportedOperationException(
                "GenericWebApplicationContext does not support getConfigLocations()");
    }

    @Override
    public void setConfigLocations(String... configLocations) {
        if (!ObjectUtils.isEmpty(configLocations)) {
            throw new UnsupportedOperationException(
                    "GenericWebApplicationContext does not support setConfigLocations(). " +
                            "Do you still have a 'contextConfigLocations' init-param set?");
        }
    }

}
